第三章 文件查找
这一章的目标是向你介绍如何在VIM里进行文件搜索。学会如何快速搜索文件会让你用VIM的生产力大幅提升。当我还在想象如何快速搜索文件的时候,我已经做出了全时间使用VIM的改变。
这章分成两个部分:如何不用任何插件搜索文件 和 如何用fzf.vim 插件去搜索文件。让我们开始吧
打开和编辑文件
想要在VIM里打开一个文件,你可以用:edit
:edit file.txt
如果file.txt
存在,它就会打开file.txt
的buffer。如果不存在,它就会为这个文件创建一个新的buffer。
:edit
可以用<tab>
自动补全,举个例子,如果你的文件是Rails的用户控制器,文件路径是./app/controllers/users_controllers.rb
。你可以用<tab>
来快速找到
:edit a<tab>c<tab>u<tab>
:edit
支持通配符语法*
匹配任意文件在当前文件夹。如果你想要在当前文件夹找一个.yml
后缀的文件,可以
:edit *.yml<tab>
VIM会给你一个所有在当前文件夹的.yml
后缀的文件列表。
你可以用**
进行递归式搜索,如果你想要在你的项目里找到所有的*.md
文件,但你不知道它们在哪一个文件夹里,你就可以
:edit **/*.md<tab>
:edit
可以运行netrw
,一个VIM内置的文件浏览功能。想要调出这个界面要用:edit
去打开一个文件夹而不是文件。
:edit .
:edit test/unit/
用Find来查找文件
你可以用:find
来查找文件,举个例子
:find package.json
:find app/controlers/users_controller.rb
这个指令同样可以用<tab>
补全,例子就不举了。
你可以觉得:find
和:edit
是一样的,那它们到底有什么不同呢?
Find 和 Path
不同的地方就是:find
通过path
来查找文件。让我们来学习一点path
的知识。一旦你学会了如何修改你的路径,:find
会成为一个强大的工具,查看当前的路径,你可以
:set path?
一般情况,你的路径可能会是这样
path=.,/usr/include,,
.
的意思是在当前文件所在的文件夹里搜索。,
的意思是在当前的文件夹搜索、/usr/include
是C语言头文件的文件夹。
前两个点很重要,第三个点现在可以忽略。最重要的是你可以自己修改你的路径,让我们来假设下面是你的工程结构
app/
assets/
controllers/
application_controller.rb
comments_controller.rb
users_controller.rb
...
如果你想从根目录前往user_controller.rb
,你你需要穿过很多个文件夹(按一堆tab键)。当我们在写一个框架的时候,你大概会花百分之九十的时间在某个文件夹里,在这种情况下,你只想少敲点按键打开在controllers
文件夹里的文件。而path
能让这个过程缩短很多。
你需要在你的路径里面添加app/controllers/
:set path+=app/controllers/
现在你的路径已经更新了,当你输入:find u<Tab>
,VIM现在会直接在app/controllers/
文件夹里面找开头是u的文件。
如果你在controllers/
又嵌套了文件夹,像app/controllers/account/users_controller.rb
,VIM不会找到user_controllers
,你需要改变增加的地址,:set path+=app/controllers/**
,这样自动补全就会帮你找到users_controller.rb
。这就很棒了,你现在只需要按一下tab键就可以找到user_controllers
了。
你可以在想在path上添加整个工程文件的地址,这样你按下tab键就可以访问到所有的文件。像这样
:set path+=&PWD/**
$PWD
是当前的工作文件夹,如果你想添加完整的项目到path时,你的项目最好规模不是很大,当你的文件夹里文件很多的时候,会发现也很难处理。所以我推荐你只加上经常访问的文件路径。
你可以在你的vimrc里面添加set path+={your-path-here}
,更新path就只需要短短你几秒,却能大大节省你的时间。
用Grep来文件内搜索
如果你需要在文件内查找(在很多文件里面找一个词语),你可以用grep。VIM又两种方法打开grep
- 内部grep(
:vim
,没错,是:vimgrep
的缩写)。 - 外部grep(
:grep
)。
让我们先来学习一下内部grep,:vim
又下面这样的语法:
:vim /pattern/ file
/pattern/
是你要查找的内容file
是文件名,你也可以输入多个文件名。VIM会在输入的文件里面查找你要找的内容。类似:find
,你也可以用*
和**
这两个通配符。
举个例子,当你想要在app/controllers/
的所有ruby文件中查找”breakfast”时,你可以
:vim /breakfast/ app/controllers/**/*.rb
在运行这条指令之后,你会被重定向到第一个查找结果,VIM的vim
搜索用了quickfix
操作。想要看到所有的搜索结果,需要运行:copen
,这会打开一个quickfix
窗口。下面有一些有用的quickfix
指令。
:copen "打开quickfix窗口
:cclose "关闭quickfix窗口
:cnext "进入下一个查找结果
:cprevious "进入前一个查找结果
:colder "进入之前的查找列表
:cnewer "进入新的查找列表
如果你想了解更多quickfix,可以在:h quickfix
查看。
你可能注意到使用internal grep(:vim
)在你有很多匹配数量的时候查找很慢。这是因为VIM把搜索目标都读进了内存。VIM像要编辑一样地加载了每个匹配文件。如果有很多查找目标,VIM就会花费大量地内存空间。
让我们再来看看external grep。默认情况下,用的是grep
命令行指令。如果你想要在一个app/controllers
路径地ruby文件里查找”lunch”,你就可以用
:grep -R "lunch" app/controllers/
你会发现与:vim
指令不同,grep用的是”pattern”,而不知/pattern/,它也会在quickfix
里显示。
VIM用grepprg
变量来确认哪些外部程序要在:grep
运行时运行。所以你没有必要用命令行地grep
指令。等会我会向你展示如何改变默认地grep外部程序。
用Netrw来浏览文件系统
netrw
是VIM内置地文件浏览系统。它经常用来查看项目的层次结构。想要运行netrw
,你需要在你的.vimrc
里面添加这两个选项
set nocp
filetype plugin on
因为netrw
是很庞大的体系,所以我只会教一些基础的语法,但是这就足够你起步使用了。当你运行vim或者在后面加一些文件夹的指令就能打开netrw
了
vim .
vim src/client/
vim app/controllers/
想要在已经运行了VIM打开netrw
的话,可以用:edit
来打开它
:edit .
:edit src/client/
:edit app/controllers/
下面是一些打开netrw
窗口的其他方法。
:Explore "在当前文件里启动netrw
:Sexplore "不是黄色笑话。是split+explore,垂直分屏打开netrw
:Vexplore "这个就是水平分屏了
你可以用VIM motions来使用netrw
(在很后面的章节会介绍motion)。如果你需要创建,删除,和重命名文件/文件夹,下面有一些有用的指令。
% "创建一个新的文件
d "创建要给新的文件夹
R "重命名一个文件/文件夹
D "删除一个文件/文件夹
:h netrw
里的说明很容易理解,如果你有时间可以看一看。
如果你觉得netrw
看上去太朴素了,想要更浓的味,可以试试netrw的提升插件 vim-vinegar。如果你想找个其他的文件浏览器,NERDTree也是一个不错的改版,可以看看。
Fzf
现在你已经知道了如何用内置工具去检索查找文件了,让我们来学学用插件怎么玩。
VIM有一点做的没现代的编辑器好,那就是不管是查找文件还是在文件内查找,模糊搜索对VIM来说都有点困难。在后半章里,我会向你展示如何用fzf.vim 让搜索变得又强大又简单。
安装
首先,确保你已经下载了fzf 和ripgrep 。按照github上面的说明repo。如果你弄好了,你应该可以使用fzf
和rg
命令已经可以使用了。
Ripgrep是一个很像grep的工具(从名字上就可以看出来)。它一般情况下都会比grep快,并且有很多优点。Fzf是一个泛用的命令行模糊搜索指令。你可以把它和其他指令一起使用,包括ripgrep。它们一起使用后,就会成为一个强大的文件搜索工具。
Fzf初始是不用ripgrep的,所以我们需要通过FZF_DEFAULT_COMMAND
变量告诉fzf使用ripgrep。在我的.zshrc
里(如果你是bash在你的.bashrc
)添加
if type rg &> /dev/null;then
export FZF_DEFAULT_COMMAND='rg --files'
export FZF_DEFAULT_OPTS='-m'
fi
注意第三行的FZF_DEFAULT_OPTS='-m'
,这个选项让我们可以用<Tab>
或者<Shift-Tab>
来多重选择。这行没法用在VIM上,但我认为这是很有用的选项。当你想在多个文件中执行搜索和替换时,它会派上大用场,我会在稍后做一些简单的介绍。Fzf还有很多选项,但我不会在这里将它们一 一介绍,想知道更多关于fzf的知识,可以查看man fzf
或者 fzf’s repo。首先确保有有了export FZF_DEFAULT_COMMAND='rg'
。
装完了fzf和ripgrep之后,让我们来设置fzf的插件,我这个例子中用的是vim-plug 插件管理器。但你也可以用其他插件。
把下面几句话添加进你的.vimrc
,你需要用fzf.vim插件(出自fzf作者之手)。
Plug 'junegunn/fzf.vim'
Plug 'junegunn/fzf',{'do':{-> fzf#install()}}
在你添加了这些之后,你需要在vim里运行:PlugInstall
,它会安装所有在vimrc
里那些没被安装的插件。我们这次的话会安装上fzf.vim
和fzf
。
先知道更多这个插件的信息?fzf.vim repo
Fzf语法
想要高效地使用fzf,你应该学习一些基础的fzf语法。别担心,学习清单就一点点:
^
是前缀精准匹配符。想要搜索以”welcome”开头的可以用^welcome
。$
是后缀精准匹配符。想要搜索以”my friends”结尾的,可以用friends$
。'
是一个精准匹配符。想要搜索”welcome my friends”可以用'welcome my friends
。|
是或匹配符,想要搜索”friends”和”foes”可以用friends | foes
。!
是非匹配符,当你想要搜索有”welcome”但是不能有”friends”时,welcome !friends
你可以混合这些符号,比如说,^hello | ^welcome friends$
会匹配到一句以”welcome”或”hello”开头,以”friends”结尾的话。
查找文件
想要在VIM里用fzf插件搜索文件,你可以用:Files
指令。运行:Files
之后,一个搜索面板会立即出现在你的屏幕上。
如果你用这个指令很频繁,我的建议是在vimrc里设置快捷键,我设置为Ctrl-f
nnoremap <silent><C-f> :Files<CR>
在files中搜索
想要在多个文件中搜索,你可以使用:Rg
指令。
同样的,当你很频繁地使用时,可以设置为快捷键。我设置成了 <Leader>f
(前缀键+f,前缀键貌似默认是反斜杠)。
nnoremap <silent><Leader>f :Rg<CR>
其他搜索
Fzf.vim 提供了多种搜索指令,我不会一一介绍,但你可以在这里学习它们。
下面是我的fzf键盘映射
nnoremap <silent> <Leader>b :Buffers<CR>
nnoremap <silent> <C-f> :Files<CR>
nnoremap <silent> <Leader>f :Rg<CR>
nnoremap <silent> <Leader>/ :BLines<CR>
nnoremap <silent> <Leader>' :Marks<CR>
nnoremap <silent> <Leader>g :Commits<CR>
nnoremap <silent> <Leader>H :Helptags<CR>
nnoremap <silent> <Leader>hh :History<CR>
nnoremap <silent> <Leader>h: :History:<CR>
nnoremap <silent> <Leader>h/ :History/<CR>
用Rg取代Grep
之前提到过两种在多个文件内搜索地指令:vim
和:grep
,